package com.tianquan.miracle.util;

import com.tianquan.miracle.annotation.Component;
import com.tianquan.miracle.annotation.Configuration;
import com.tianquan.miracle.annotation.MiracleFastApplication;
import com.tianquan.miracle.aop.annotation.After;
import com.tianquan.miracle.aop.AopEntity;
import com.tianquan.miracle.aop.annotation.Around;
import com.tianquan.miracle.aop.annotation.Before;
import com.tianquan.miracle.context.ApplicationContext;

import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

/**
 * @Author: tianquan
 * @date: 2024-09-29  12:17
 * @Description: 框架工具类
 */
public class MiracleUtils {

    private static ApplicationContext applicationContext;

    private static final ClassLoader CLASS_LOADER = Thread.currentThread().getContextClassLoader();

    public static void init(ApplicationContext applicationContext) {
        MiracleUtils.applicationContext = applicationContext;
    }

    public static ClassLoader getClassLoader() {
        return CLASS_LOADER;
    }

    /**
     * 根据类生成bean名称。
     *
     * @param clazz 类对象。
     * @return 生成的bean名称。
     */
    public static String createBeanName(Class<?> clazz) {
        // 给代理对象生成beanName
        if (clazz.getName().contains("$")) {
            return clazz.getSimpleName().toLowerCase();
        }
        // 普通对象
        String name = clazz.getName();
        String beanName = name.substring(name.lastIndexOf(".") + 1);
        return beanName.substring(0, 1).toLowerCase() + beanName.substring(1);
    }

    /**
     * 判断类上是否有指定的注解，如果有则返回该注解对象。
     *
     * @param clazz      类对象。
     * @param annotation 注解类。
     * @return 注解对象。
     */
    public static <T> T isExist(Class<?> clazz, Class<T> annotation) {
        T object = null;
        isExist(clazz, annotation, object);
        return (T) object;
    }

    private static <T> void isExist(Class<?> clazz, Class<T> annotation, Object object) {
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null) {
            return;
        }
        for (Annotation a : annotations) {
            isExist(a.getClass(), annotation, object);
            if (a.getClass() == annotation) {
                object = a;
                return;
            }
        }
    }

    /**
     * 扫描类上的注解，返回第一个匹配的注解对应的bean名称。
     *
     * @param clazz 类对象。
     * @return 匹配的注解对应的bean名称。
     */
    public static String sanComponentAndCreateBeanName(Class<?> clazz) {
        Component component = clazz.getAnnotation(Component.class);
        if (component != null) {
            String value = component.value();
            if (value.isEmpty()) {
                return createBeanName(clazz);
            }
            return value;
        }
        Configuration configuration = clazz.getAnnotation(Configuration.class);
        if (configuration != null) {
            return MiracleUtils.createBeanName(clazz);
        }
        MiracleFastApplication miracleFastApplication = clazz.getAnnotation(MiracleFastApplication.class);
        if (miracleFastApplication != null) {
            return MiracleUtils.createBeanName(clazz);
        }
        return null;
    }

    /**
     * 获取application.properties文件的内容。
     *
     * @return application.properties文件的内容。
     */
    public static Properties getProperties() {
        Properties properties = new Properties();
        InputStream inputStream = null;
        try {
            inputStream = CLASS_LOADER.getResourceAsStream("application.properties");
            properties.load(inputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return properties;
    }

    /**
     * 判断当前类是否是目标类的子类。
     *
     * @param clazz       当前类。
     * @param parentClass 目标类。
     * @return 如果当前类是目标类的子类，则返回true，否则返回false。
     */
    public static boolean isDescendantClass(Class<?> clazz, Class<?> parentClass) {
        if (clazz == null) {
            return false;
        }
        return clazz.equals(parentClass) || isDescendantClass(clazz.getSuperclass(), parentClass);
    }

    /**
     * 判断当前类是否与目标类或接口有关联。
     *
     * @param clazz                  当前类。
     * @param targetClassOrInterface 目标类或接口。
     * @return 如果当前类与目标类或接口有关联，则返回true，否则返回false。
     */
    public static boolean isRelatedTo(Class<?> clazz, Class<?> targetClassOrInterface) {
        if (clazz == null) {
            return false;
        }
        if (clazz.equals(targetClassOrInterface)) {
            return true;
        }
        List<Class<?>> interfaces = Arrays.asList(clazz.getInterfaces());
        for (Class<?> anInterface : interfaces) {
            if (isRelatedTo(anInterface, targetClassOrInterface)) {
                return true;
            }
        }
        return isRelatedTo(clazz.getSuperclass(), targetClassOrInterface);
    }

    /**
     * 根据bean名称获取Spring容器中的bean。
     *
     * @param beanName bean名称。
     * @return 对应的bean对象。
     */
    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

    /**
     * 根据类获取Spring容器中的bean。
     *
     * @param clazz 类对象。
     * @return 对应的bean对象。
     */
    public static Object getBean(Class<?> clazz) {
        return applicationContext.getBean(clazz);
    }

    /**
     * 在Spring容器中注册一个bean。
     *
     * @param name bean名称。
     * @param bean bean对象。
     */
    public static void registerBean(String name, Object bean) {
        applicationContext.registerBean(name, bean);
    }

    /**
     * 检查AOP实体中的注解方法是否正确。
     *
     * @param aopEntity  AOP实体。
     * @param methodName 方法名称。
     */
    public static void cheekAnnotation(AopEntity aopEntity, String methodName) {
        Method beforeMethod = aopEntity.getBeforeMethod();
        Method afterMethod = aopEntity.getAfterMethod();
        Method aroundMethod = aopEntity.getAroundMethod();
        if (beforeMethod != null) {
            MiracleUtils.annotationException(beforeMethod.getAnnotation(Before.class), methodName);
        }
        if (afterMethod != null) {
            MiracleUtils.annotationException(afterMethod.getAnnotation(After.class), methodName);
        }
        if (aroundMethod != null) {
            MiracleUtils.annotationException(aroundMethod.getAnnotation(Around.class), methodName);
        }
    }

    /**
     * 检查注解的值是否为空，并验证注解的方法名称是否与传入的方法名称一致。
     *
     * @param annotation 注解对象。
     * @param methodName 方法名称。
     */
    public static void annotationException(Annotation annotation, String methodName) {
        Object invoke = null;
        try {
            invoke = annotation.getClass().getMethod("value").invoke(annotation);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (invoke == null) {
            throw new RuntimeException("The value of the annotation cannot be empty");
        }
        if (!invoke.toString().replace("()", "").equals(methodName)) {
            throw new RuntimeException("The tangent method name is different from the tangent method name");
        }
    }

    /**
     * 解析方法上的注解。
     *
     * @param method     方法对象。
     * @param annotation 注解类。
     * @return 注解对象。
     */
    public static <T extends Annotation> T parseMethod(Method method, Class<T> annotation) {
        return method.getAnnotation(annotation);
    }

}
